package com.gitee.l0km.codegen.thrift;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.gitee.l0km.aocache.annotations.AoCacheable;
import com.gitee.l0km.codegen.base.CodeGenUtils;
import com.gitee.l0km.codegen.thrift.ThriftServiceDecoratorConfiguration.TaskType;
import com.gitee.l0km.com4j.base.NameStringUtils;
import com.gitee.l0km.com4j.basex.bean.BeanRelativeUtilits;
import com.gitee.l0km.xthrift.base.BaseThriftUtils;
import com.gitee.l0km.xthrift.base.exception.ServiceRuntimeException;
import com.gitee.l0km.xthrift.base.metadata.DecoratorThriftEnumMetadata;
import com.gitee.l0km.xthrift.base.metadata.DecoratorThriftEnumMetadata.ExtensiveEnumItem;
import com.gitee.l0km.xthrift.base.metadata.DecoratorThriftStructMetadata;
import static com.gitee.l0km.xthrift.base.metadata.Decorators.javadocCommentProviderFactory;
import com.gitee.l0km.xthrift.base.metadata.ErpcProxyReturnCode;
import com.gitee.l0km.xthrift.base.metadata.ErpcType;
import com.gitee.l0km.xthrift.base.metadata.ThriftCatalogWithTransformer;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;

/**
 * @author guyadong
 *
 */
public class ThriftSchema implements ThriftConstants {
	private final Set<ThriftStructDecorator> thriftStructDecorators = Sets.newLinkedHashSet();
	private final Set<ThriftStructDecorator> thriftStructs = Sets.newLinkedHashSet();
	private final Set<ErpcType> collectionStructs = Sets.newTreeSet();
	private final List<ThriftServiceDecorator<?>> thriftServiceDecorators = Lists.newLinkedList();
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public ThriftSchema(Map<Class<?>, Class<?>> interfaceClasses) {
		for(Entry<Class<?>, Class<?>> entry:interfaceClasses.entrySet()){
			Class<?> interfaceClass = entry.getKey();
			Class<?> refClass = entry.getValue();
			if(refClass == ThriftServiceDecoratorConfiguration.DEF_REF_CLASS){
				refClass = null;
			}
			ThriftServiceDecorator service = new ThriftServiceDecorator(interfaceClass,refClass);
			thriftServiceDecorators.add(service);
		}
		if(!compile()){
			throw new IllegalStateException("compile fail");
		}
	}
	public List<ThriftStructDecorator> getAllStructDecorators() {
		return Lists.newArrayList(thriftStructDecorators);
	}
	public List<ThriftStructDecorator> getExceptionDecorators(){
		return Lists.newArrayList(Sets.filter(thriftStructDecorators, ThriftStructDecorator::isException));
	}
	public List<ThriftStructDecorator> getEnumDecorators(){
		return Lists.newArrayList(Sets.filter(thriftStructDecorators, ThriftStructDecorator::isEnum));
	}
	public List<ThriftStructDecorator> getBeanDecorators(){
		return Lists.newArrayList(Sets.filter(thriftStructDecorators, ThriftStructDecorator::isBean));
	}
	public List<ThriftServiceDecorator<?>> getThriftServiceDecorators() {
		return thriftServiceDecorators;
	}
	private boolean compile(){
		for(ThriftServiceDecorator<?> newSourceInfo:getThriftServiceDecorators()){
			if (!newSourceInfo.compile()){
				return false;
			}
			thriftStructDecorators.addAll(newSourceInfo.getDecorateTypes());
			thriftStructs.addAll(newSourceInfo.getThriftTypes());
		}
		for(ThriftStructDecorator newSourceInfo:getAllStructDecorators()){
			if (!newSourceInfo.compile()){
				return false;
			}
			thriftStructDecorators.addAll(newSourceInfo.getDecorateTypes());
			thriftStructs.addAll(newSourceInfo.getThriftTypes());
		}
		return true;
	}
	public boolean isThriftBuildinType(Type type){
		return BaseThriftUtils.isThriftBuildinType(type);
	}
	public boolean isCastType(Type type){
		return  BaseThriftUtils.isCastType(type);
	}

	private static final Map<String,Class<?>> PRIMITIVE_NUM_TYPES = 
			ImmutableMap.<String,Class<?>>builder()
				.put("byte", byte.class)
				.put("short", short.class)
				.put("int", int.class)
				.put("long", long.class)
				.put("float", float.class)
				.put("double", double.class)
				.build();
	/**
	 * 检查是否可以从一个强制类型转换到另一个类型。<p>
	 * 该方法会判断源类型和目标类型是否满足特定的转换条件，<br>
	 * 目前主要检查基本数字类型之间的转换条件。<br>
	 * 示例：源类型是基本数字类型int， 如果目标类型不是包装类,比如为：long，则允许直接类型转换。<br>
	 * 代码示例：
	 * <pre>
	 * int i = 1;
	 * long l = (long)i;
	 * </pre>
	 * @param from 源类型
	 * @param to 目标类型名
	 * @return 如果可以转换返回true，否则返回false
	 * @since 3.6.0
	 */
	public boolean canCast(Type from, Type to){
		if(from instanceof Class && to instanceof Class<?>){
			Class<?> clazz = (Class<?>)from;
			Class<?> targetClass = (Class<?>)to;
			if(clazz.isPrimitive() && targetClass.isPrimitive() 
					&& Number.class.isAssignableFrom(Primitives.wrap(clazz))
					&& Number.class.isAssignableFrom(Primitives.wrap(targetClass))){
				/** 
				 * 示例：
				 * 源类型是基本数字类型int， 如果目标类型不是包装类,比如为：long，则允许直接类型转换 
				 * int i = 1;
				 * long l = (long)i;
				 */
				return true;
			}
		}
		return false;
	}
	/**
	 * 检查是否可以从一个强制类型转换到另一个类型。<p>
	 * 该方法会判断源类型和目标类型是否满足特定的转换条件，<br>
	 * @param from 源类型
	 * @param to 目标类型名
	 * @return 如果可以转换返回true，否则返回false
	 * @since 3.6.0
	 */
	public boolean canCast(Type from, String to){
		if(from instanceof Class && PRIMITIVE_NUM_TYPES.containsKey(to)){
			return canCast(from, PRIMITIVE_NUM_TYPES.get(to));
		}
		return false;
	}
	/**
	 * 检查是否可以从一个强制类型转换到另一个类型。<p>
	 * 该方法会判断源类型和目标类型是否满足特定的转换条件，<br>
	 * @param from 源类型名
	 * @param to 目标类型
	 * @return 如果可以转换返回true，否则返回false
	 * @since 3.6.0
	 */
	public boolean canCast(String from, Type to){
		if(to instanceof Class && PRIMITIVE_NUM_TYPES.containsKey(from)){
			return canCast(PRIMITIVE_NUM_TYPES.get(from), to);
		}
		return false;
	}
	
	public boolean isDecoratorType(final Type type) {
		return getDecoratorType(type) != null;
	}
	public ThriftStructDecorator getDecoratorType(Type type) {
		return Iterables.tryFind(thriftStructDecorators, struct->struct.getBaseClass().equals(type)).orNull();
	}
	public boolean isThriftStruct(Type type){
		return BaseThriftUtils.isThriftStruct(type);
	}
	public boolean isPrimitivefloat(Type type){
		return BaseThriftUtils.isPrimitivefloat(type);
	}
	public boolean isfloat(Type type){
		return BaseThriftUtils.isfloat(type);
	}
	public boolean isPrimitiveArray(Type type){
		return BaseThriftUtils.isPrimitiveArray(type);
	}
	/**
	 * @since 3.6.0
	 */
	public static boolean needGenericTransformer(Type type) {
		return BaseThriftUtils.needGenericTransformer(type);
	}
	public boolean isMap(Type type){
		return TypeToken.of(type).getRawType() == Map.class;
	}
	public boolean isList(Type type){
		return TypeToken.of(type).getRawType() == List.class;
	}
	public boolean isSet(Type type){
		return TypeToken.of(type).getRawType() == Set.class;
	}
	public boolean isCollection(Type type){
		return isList(type) || isSet(type);
	}
	public boolean isArray(Type type){
		return isList(type) || isSet(type);
	}
	public boolean isBinary(Type type){
		return type == byte[].class;
	}
	public final Type wrap(Type type){
		return TypeToken.of(type).wrap().getType();
	}
	/**
	 * 对thrift IDL 关键字(binary,list,map,set,i32...)转义,以确保生成的IDL文件中成员名和参数名与thrift关键字不冲突
	 * @param name
	 */
	public String escapeThrift(String name){
		return NameStringUtils.isThriftReserved(name)? name + "_" : name;
	}
	/**
	 * 字段名转义,避免java,thrift关键字冲突
	 * @param name
	 */
	public String escapeField(String name){
		if(NameStringUtils.isJavaReserved(name)){
			return "_" + name;
		}else {
			return escapeThrift(name);
		}
	}
	public static boolean isIsLocalMethod(Method method){
		return BaseThriftUtils.isIsLocalMethod(method);
	}
	public static boolean isIsLocalMethod(com.gitee.l0km.codegen.base.Method method){
		return BaseThriftUtils.isIsLocalMethod(method.delegate());
	}
	public Type[] getActualTypeArguments(Type type){
		if( type instanceof ParameterizedType){
			return ((ParameterizedType)type).getActualTypeArguments();
		}
		return new Type[0];
	}
	public Class<?> getServiceRuntimeExceptionClass(){
		return ServiceRuntimeException.class;
	}

	public String toStubType(Class<?> clazz){
		checkArgument(null !=clazz,"clazz is null");
		if(isThriftStruct(clazz) || Enum.class.isAssignableFrom(clazz) || this.isDecoratorType(clazz)){
			return ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage() + "." + clazz.getSimpleName();
		}
		throw new IllegalArgumentException(String.format("%s is not thrift stub type", clazz.getName()));
	}
	public String toStubCxxType(Class<?> clazz){
		return CxxHelper.cxxNamespace("::"+toStubType(clazz),true);
	}
	/**
	 * 返回指定包名下的所有{@link ThriftStructDecorator}对象,按类名自然排序,没有找到则返回空表
	 * @param pkg
	 */
	public List<ThriftStructDecorator> getThriftStructDecorator(final String pkg){
		ImmutableList<ThriftStructDecorator> list = FluentIterable.from(this.thriftStructDecorators)
			.filter(struct->struct.getBaseClass().getPackage().getName().equals(pkg)).toList();
		return Ordering.natural().sortedCopy(list);
	}
	/**
	 * 根据{@link ThriftStructDecorator}对象之间的依赖关系返回{@link ThriftStructDecorator}队列
	 * @param thriftStructIncluded 是否包含thrift struct 类
	 */
	protected List<ThriftStructDecorator> getGenSequence(boolean thriftStructIncluded){
		List<ThriftStructDecorator> types = getAllStructDecorators();
		if(thriftStructIncluded){
			types.addAll(thriftStructs);
			for(Class<?> structType : ThriftServiceDecoratorConfiguration.INSTANCE.getExtStructs()){
				types.add(TypeHelper.makeThriftStructDecorator(structType));
			}
		}
		List<ThriftStructDecorator> list = BeanRelativeUtilits.sortByField(types,"name");
		//List<ThriftStructDecorator> list = getAllStructDecorators();
		ArrayDeque<ThriftStructDecorator> seq = new ArrayDeque<>();
		for(ThriftStructDecorator struct:list){
			traverseMember(struct,seq, thriftStructIncluded);
		}
		return Lists.newArrayList(seq.descendingIterator());
	}
	/**
	 * 生成结构类型序列,确保类型的成员类型如果为结构类型定义在父类型之前
	 * @param parent 父类型
	 * @param seq 生成的结构类型序列
	 * @param thriftStructIncluded
	 */
	private void traverseMember(ThriftStructDecorator parent,Deque<ThriftStructDecorator> seq, boolean thriftStructIncluded){
		if(!seq.contains(parent)){
			List<ThriftStructDecorator> decoratorMembers = BeanRelativeUtilits.sortByField(parent.getDecorateTypes(),"name");
			for(ThriftStructDecorator struct:decoratorMembers){
				traverseMember(struct,seq, thriftStructIncluded);
			}
			if(thriftStructIncluded){
				List<ThriftStructDecorator> thriftMembers = BeanRelativeUtilits.sortByField(parent.getThriftTypes(),"name");
				for(ThriftStructDecorator struct:thriftMembers){
					traverseMember(struct,seq, thriftStructIncluded);
				}
			}
			seq.push(parent);
		}
	}
	/**
	 * 根据{@link ThriftStructDecorator}对象之间的依赖关系返回以package为key的 {@link ListMultimap}映射
	 * @param thriftStructIncluded 是否包含thrift struct 类
	 */
	public ListMultimap<String,Collection<ThriftStructDecorator>> getStructGenSequenceAsMultimap(boolean thriftStructIncluded){
		List<ThriftStructDecorator> seqlist = getGenSequence(thriftStructIncluded);
		seqlist.add(new ThriftStructDecorator(getServiceRuntimeExceptionClass()));
		LinkedListMultimap<String, Collection<ThriftStructDecorator>> seq = LinkedListMultimap.<String, Collection<ThriftStructDecorator>>create();
		String curPkg=null;
		List<ThriftStructDecorator> sub = null;
		for(ThriftStructDecorator struct:seqlist){
			String pkg = struct.getPackage();
			if(!pkg.equals(curPkg)){
				if(null !=curPkg){
					seq.put(curPkg, sub);
				}
				sub = Lists.newLinkedList();
				curPkg = struct.getPackage();
			}
			sub.add(struct);
		}
		if(null !=curPkg){
			seq.put(curPkg, sub);
		}
		return seq;
	}
	public boolean isUseStringInServices(){
		return Iterables.tryFind(thriftServiceDecorators, ThriftServiceDecorator::isUseStringInService).isPresent();
	}
	public boolean isUseBinaryInServices(){
		return Iterables.tryFind(thriftServiceDecorators, ThriftServiceDecorator::isUseBinaryInService).isPresent();
	}
	public boolean isUseStringInTypes(){
		return Iterables.tryFind(thriftStructDecorators, ThriftStructDecorator::isUseString).isPresent();
	}
	public boolean isUseBinaryInTypes(){
		return Iterables.tryFind(thriftStructDecorators, ThriftStructDecorator::isUseBinary).isPresent();
	}
	public boolean isUseMapInTypes(){
		return Iterables.tryFind(thriftStructDecorators, ThriftStructDecorator::isUseMap).isPresent();
	}
	public boolean isUseSetInTypes(){
		return Iterables.tryFind(thriftStructDecorators, ThriftStructDecorator::isUseSet).isPresent();
	}
	public boolean isUseVectorInTypes(){
		return Iterables.tryFind(thriftStructDecorators, ThriftStructDecorator::isUseVector).isPresent() || isSql2java4x();
	}
	/**
	 * @since 2.3.5
	 */
	@AoCacheable
	public boolean isSql2java4x(){
		return thriftStructs.stream().anyMatch(ThriftStructDecorator::isSql2java4x);
	}
	public boolean isUseExceptionInTypes(){
		return Iterables.tryFind(thriftStructDecorators, ThriftStructDecorator::isException).isPresent();
	}
	@AoCacheable
	public boolean isCanMove(CxxType cxxType){
		if(null == cxxType){
			return false;
		}
		CxxTypeMeta uiType = cxxType.getUiType();
		if(uiType.isCanMove() && !uiType.isStruct()){
			return true;
		}
		ThriftStructDecorator memSturct = getDecoratorType(cxxType.getJavaType());
		return null !=memSturct && memSturct.isHasCanMoveField();
	}
	@AoCacheable
	public boolean isCommonType(final Type type){
		if(type instanceof Class<?>){
			Set<String> commonTypes = ThriftServiceDecoratorConfiguration.INSTANCE.getCommonTypes();
			return FluentIterable.from(commonTypes).anyMatch(str->{
					Class<?> clazz = (Class<?>)type;
					return clazz.getName().equals(str) 
							|| clazz.getSimpleName().equals(str)
							|| clazz.getPackage().getName().startsWith(str);
				});
		}
		return false;
	}
	public String getProgramName(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getProgramName();
	}
	public String getPortPrefix(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getPortPrefix();
	}
	/**
	 * @param thriftServiceClasses 要设置的 thriftServiceClasses
	 * @return 当前对象
	 */
	public ThriftSchema setThriftServiceClasses(Map<Class<?>, Class<?>> thriftServiceClasses) {
		if(thriftServiceClasses != null){
			for(ThriftServiceDecorator<?> decorator:thriftServiceDecorators){
				decorator.setThriftServiceClass(thriftServiceClasses.get(decorator.getInterfaceClass())).erpcProxyInit();
			}
		}
		return this;
	}
	public DecoratorThriftEnumMetadata<?> getThriftEnumMetadata(Type type){
		if(type instanceof Class<?>){
			return (DecoratorThriftEnumMetadata<?>) ThriftCatalogWithTransformer.CATALOG.getThriftEnumMetadata((Class<?>) type);
		}
		return null;
	}
	public DecoratorThriftEnumMetadata<?> getThriftEnumMetadata(String className) throws ClassNotFoundException{
		return getThriftEnumMetadata(CodeGenUtils.forName(className));
	}
	public DecoratorThriftStructMetadata getThriftStructMetadata(Type type){
		return (DecoratorThriftStructMetadata) ThriftCatalogWithTransformer.CATALOG.getThriftStructMetadata(type);
	}
	public DecoratorThriftStructMetadata getThriftStructMetadata(String className) throws ClassNotFoundException{
		return getThriftStructMetadata(CodeGenUtils.forName(className));
	}
	/**
	 * @return collectionStructs
	 */
	public List<ErpcType> getCollectionStructs() {
		return Lists.newArrayList(collectionStructs);
	}
	/**
	 * @return collectionStructs
	 */
	public List<ErpcType> getCollectionStructs(final boolean structElement) {
		Iterable<ErpcType> filtered = Iterables.filter(collectionStructs,
				c->{
			boolean isStruct = isThriftStruct(c.getValueType().getJavaType());
			return  structElement ? isStruct : !isStruct;
		});
		ArrayList<ErpcType> types = Lists.newArrayList(filtered);		
		return types;
	}
	
	public int getExistsListString(){
		return Iterables.tryFind(collectionStructs, c->c.getValueType().getProtocolType().isString()).isPresent() ? 1 : 0;
	}
	public int getExistsListBinary(){
		return Iterables.tryFind(collectionStructs, c->c.getValueType().getProtocolType().isBinary()).isPresent() ? 1 : 0;
	}
	public ThriftSchema erpcProxyInit(){
		if(TaskType.ERPC_PROXY.equals(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType())){
			for(ThriftServiceDecorator<?> newSourceInfo:getThriftServiceDecorators()){
				collectionStructs.addAll(newSourceInfo.getCollectionTypes());
			}
			DecoratorThriftEnumMetadata.beforeExtensiveEnumItems = createBeforeExtensiveEnumItem();
			// 添加到列表头部
			thriftStructs.add(new ThriftStructDecorator(ErpcProxyReturnCode.class));
		}
		return this;
	}
	private ImmutableMap<Class<? extends Enum<?>>, List<ExtensiveEnumItem>> createBeforeExtensiveEnumItem(){
		ArrayList<ExtensiveEnumItem> items = Lists.newArrayList();
		for(ThriftStructDecorator type:thriftStructDecorators){
			Class<?> javaType = type.getBaseClass();
			if(javaType instanceof Class<?> && Exception.class.isAssignableFrom(javaType)){
				 ImmutableList<String> comment = null;
				if(javadocCommentProviderFactory != null){
					comment =  ImmutableList.copyOf(javadocCommentProviderFactory.apply(javaType).commentOfClass());
				}
				items.add(new ExtensiveEnumItem(javaType.getSimpleName(),null,comment));
			}
		}
		return ImmutableMap.<Class<? extends Enum<?>>, List<ExtensiveEnumItem>>of(ErpcProxyReturnCode.class,items);
	}
	public int getErpcForwardPort(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getErpcForwardPort();
	}
	public int getErpcProxyPort(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getErpcProxyPort();
	}
	public int getDefaultMaxLength(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getDefaultMaxLength();
	}
	public int getErrmsgMaxLength(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getErrmsgMaxLength();
	}
	public int getBinaryOutputSize(){
		return ThriftServiceDecoratorConfiguration.INSTANCE.getBinaryOutputSize();
	}
}